///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*	Contains a simple container class.
*	\file		LaborContainer.h
*	\author		Pierre Terdiman
*	\date		February, 5, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __LABORCONTAINER_H__
#define __LABORCONTAINER_H__

#include "LaborMemoryMacros.h"
#include "LaborFPU.h"
#include "../LaborCoreDef.h"
NAMESPACE_LABOR3D_BEGIN
#define CONTAINER_STATS

enum ELFindMode
{
	FIND_CLAMP,
	FIND_WRAP,

	FIND_FORCE_DWORD = 0x7fffffff
};

class  LABORCORE_API CLContainer
{
public:
	// Constructor / Destructor
	CLContainer();
	CLContainer(const CLContainer& object);
	CLContainer(unsigned int size, float growth_factor);
	~CLContainer();
	// Management
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	A O(1) method to add a value in the container. The container is automatically resized if needed.
	*	The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation
	*	costs a lot more than the call overhead...
	*
	*	\param		entry		[in] a unsigned int to store in the container
	*	\see		Add(float entry)
	*	\see		Empty()
	*	\see		Contains(unsigned int entry)
	*	\return		Self-Reference
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	CLContainer&		add(unsigned int entry)
	{
		// Resize if needed
		if(mCurNbEntries==mMaxNbEntries)	resize();

		// Add new entry
		mEntries[mCurNbEntries++]	= entry;
		return *this;
	}

	inline	CLContainer&		add(const unsigned int* entries, unsigned int nb)
	{
		// Resize if needed
		if(mCurNbEntries+nb>mMaxNbEntries)	resize(nb);

		// Add new entry
		CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(unsigned int));
		mCurNbEntries+=nb;
		return *this;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	A O(1) method to add a value in the CLContainer. The CLContainer is automatically resized if needed.
	*	The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation
	*	costs a lot more than the call overhead...
	*
	*	\param		entry		[in] a float to store in the CLContainer
	*	\see		Add(unsigned int entry)
	*	\see		Empty()
	*	\see		Contains(unsigned int entry)
	*	\return		Self-Reference
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	CLContainer&		add(float entry)
	{
		// Resize if needed
		if(mCurNbEntries==mMaxNbEntries)	resize();

		// Add new entry
		mEntries[mCurNbEntries++]	= IR(entry);
		return *this;
	}

	inline	CLContainer&		add(const float* entries, unsigned int nb)
	{
		// Resize if needed
		if(mCurNbEntries+nb>mMaxNbEntries)	resize(nb);

		// Add new entry
		CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float));
		mCurNbEntries+=nb;
		return *this;
	}

	//! Add unique [slow]
	inline	CLContainer&		addUnique(unsigned int entry)
	{
		if(!contains(entry))	add(entry);
		return *this;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Clears the CLContainer. All stored values are deleted, and it frees used ram.
	*	\see		Reset()
	*	\return		Self-Reference
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	CLContainer&		empty();

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Resets the CLContainer. Stored values are discarded but the buffer is kept so that further calls don't need resizing again.
	*	That's a kind of temporal coherence.
	*	\see		Empty()
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	void			reset()
	{
		// Avoid the write if possible
		// ### CMOV
		if(mCurNbEntries)	mCurNbEntries = 0;
	}

	// HANDLE WITH CARE
	inline	void			forceSize(unsigned int size)
	{
		mCurNbEntries = size;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Sets the initial size of the CLContainer. If it already contains something, it's discarded.
	*	\param		nb		[in] Number of entries
	*	\return		true if success
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool			setSize(unsigned int nb);

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Refits the CLContainer and get rid of unused bytes.
	*	\return		true if success
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool			refit();

	// Checks whether the CLContainer already contains a given value.
	bool			contains(unsigned int entry, unsigned int* location=0) const;
	// Deletes an entry - doesn't preserve insertion order.
	bool			deleteEntry(unsigned int entry);
	// Deletes an entry - does preserve insertion order.
	bool			deleteKeepingOrder(unsigned int entry);
	//! Deletes the very last entry.
	inline	void			deleteLastEntry()						{ if(mCurNbEntries)	mCurNbEntries--;			}
	//! Deletes the entry whose index is given
	inline	void			DeleteIndex(unsigned int index)					{ mEntries[index] = mEntries[--mCurNbEntries];	}

	// Helpers
	CLContainer&		findNext(unsigned int& entry, ELFindMode find_mode=FIND_CLAMP);
	CLContainer&		findPrev(unsigned int& entry, ELFindMode find_mode=FIND_CLAMP);
	// Data access.
	inline	unsigned int			getNbEntries()					const	{ return mCurNbEntries;					}	//!< Returns the current number of entries.
	inline	unsigned int			getEntry(unsigned int i)				const	{ return mEntries[i];					}	//!< Returns ith entry
	inline	unsigned int*			getEntries()					const	{ return mEntries;						}	//!< Returns the list of entries.

	inline	unsigned int			getFirst()						const	{ return mEntries[0];					}
	inline	unsigned int			getLast()						const	{ return mEntries[mCurNbEntries-1];		}

	// Growth control
	inline	float			getGrowthFactor()				const	{ return mGrowthFactor;					}	//!< Returns the growth factor
	inline	void			setGrowthFactor(float growth)			{ mGrowthFactor = growth;				}	//!< Sets the growth factor
	inline	bool			isFull()						const	{ return mCurNbEntries==mMaxNbEntries;	}	//!< Checks the CLContainer is full
	inline	bool			isNotEmpty()					const	{ return !( 0 == mCurNbEntries );					}	//!< Checks the CLContainer is empty

	//! Read-access as an array
	inline	unsigned int			operator[](unsigned int i)			const	{ /*ASSERT(i>=0 && i<mCurNbEntries);*/ return mEntries[i];	}
	//! Write-access as an array
	inline	unsigned int&			operator[](unsigned int i)					{ /*ASSERT(i>=0 && i<mCurNbEntries);*/ return mEntries[i];	}

	// Stats
	unsigned int			getUsedRam()					const;

	//! Operator for "Container A = Container B"
	//void			operator = (const Container& object);

#ifdef CONTAINER_STATS
	inline	unsigned int			getNbContainers()				const	{ return mNbContainers;		}
	inline	unsigned int			getTotalBytes()					const	{ return mUsedRam;			}
private:

	static	unsigned int			mNbContainers;		//!< Number of containers around
	static	unsigned int			mUsedRam;			//!< Amount of bytes used by containers in the system
#endif
private:
	// Resizing
	bool			resize(unsigned int needed=1);
	// Data
	unsigned int			mMaxNbEntries;		//!< Maximum possible number of entries
	unsigned int			mCurNbEntries;		//!< Current number of entries
	unsigned int*			mEntries;			//!< List of entries
	float			mGrowthFactor;		//!< Resize: new number of entries = old number * mGrowthFactor
};

NAMESPACE_LABOR3D_END
#endif // __LABORCONTAINER_H__
